package org.icop.frame.boost;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.icop.model.Correspondence;
import org.icop.model.MultiSet;
import org.icop.model.ValuedCorrespondence;
import org.jbpt.graph.abs.IEdge;
import org.jbpt.graph.abs.IGraph;
import org.jbpt.hypergraph.abs.IVertex;


/**
 * Boost matches based on their cardinality. That is, a certain match
 * might be proposed by different searchers. As we assume these searchers
 * to use different techniques, this hints at a good match candidate. 
 * Therefore, this booster takes a multiset of matches and returns a
 * set of matches, such that the scores are aggregated. 
 * 
 * @author matthias.weidlich
 */
public class MatchCardinalityBooster<G extends IGraph<E,V>, E extends IEdge<V>, V extends IVertex> implements Booster<G, E, V> {

	public MatchCardinalityBooster() {
		
	}
	
	/**
	 * Aggregating is done as follows: starting with the highest
	 * sim, every sim is related to the difference of 1 and the 
	 * current sim. This value is then added.
	 * 
	 * For instance, for 0.7 and 0.6, we get 0.87
	 * 
	 * 
	 * @param scores, the sim values
	 * @return the aggregated sim
	 */
	protected double aggregateScores(List<Double> scores) {
		if (scores.size() == 0)
			return 0;
		if (scores.size() == 1)
			return scores.get(0);
		
		Collections.sort(scores);
		Collections.reverse(scores);

		double result = scores.get(0);
		
		if (result == 1)
			return result;
		
		for(int i = 1; i < scores.size(); i++) {
			double diff = 1 - result;
			double boost = diff * scores.get(i);
			result += boost;
		}
		
		return result;
	}

	@Override
	public MultiSet<ValuedCorrespondence<V>> boost(G sg1, G sg2,
			MultiSet<ValuedCorrespondence<V>> mapping) {

		/*
		 * First, we need to separate matches and their score
		 */
		Map<Correspondence<V>,List<Double>> matchScores = new HashMap<Correspondence<V>,List<Double>>();
		for (ValuedCorrespondence<V> s : mapping) {
			Correspondence<V> sets = new Correspondence<V>(s.getV1s(),s.getV2s());
			if (matchScores.containsKey(sets)) {
				matchScores.get(sets).add(s.getConfidenceValue());
			}
			else {
				List<Double> scores = new ArrayList<Double>();
				scores.add(s.getConfidenceValue());
				matchScores.put(sets,scores);
			}
		}
		
		MultiSet<ValuedCorrespondence<V>> result = new MultiSet<>();
		
		/*
		 * Aggregate the scores
		 */
		for (Correspondence<V> s : matchScores.keySet()) {
			result.add(new ValuedCorrespondence<V>(s.getV1(),s.getV2(),aggregateScores(matchScores.get(s))));
		}
		
		return result;
	}

	
}
